// macro for generic device header and source file

#define HEADER(deviceName) \
	void deviceName##Out(int addr); \
	void deviceName##In(int addr); \
	int deviceName##Ready(); \
	void deviceName##Init(); \
	void deviceName##Clos(); \
	void deviceName##Ioc();

#define SOURCE(deviceName, wordNum, mode) \
	static pthread_mutex_t mutex; \
	static FILE *file = NULL; \
	extern int memory[MEM_SIZE]; \
	void deviceName##Init() { \
		if(pthread_mutex_init(&mutex, NULL)) { \
			printf("fail the initialize the mutex about the %s\n", #deviceName); \
			exit(1); \
		} \
		char buf[64]; \
		sprintf(buf, "%s.dev", #deviceName); \
		if(!(file = fopen(buf, #mode))) { \
			printf("fail to open the file stream about the %s\n", #deviceName); \
			exit(1); \
		} \
	} \
	void deviceName##Clos() { \
		pthread_mutex_lock(&mutex); \
		pthread_mutex_destroy(&mutex); \
		fclose(file); \
	} \
	int deviceName##Ready() { \
		int r = pthread_mutex_trylock(&mutex); \
		if(r) \
			return 0; \
		else { \
			pthread_mutex_unlock(&mutex); \
			return 1; \
		} \
	} \
	static void* _deviceOut(void *arg) { \
		int mask = 0x3f000000; \
		int start = (long long)arg; \
		int end = (start + wordNum <= MEM_SIZE) ? (start + wordNum) : MEM_SIZE; \
		for(int i = start; i < end; ++i) { \
			int data = memory[i]; \
			for(int j = 0; j < 5; ++j) { \
				int ch = (data & mask) >> 24; \
				ch += ' '; \
				fprintf(file, "%c", ch); \
				data <<= 6; \
			} \
		} \
		fprintf(file, "\n"); \
		pthread_mutex_unlock(&mutex); \
		return NULL; \
	} \
	static void* _deviceIn(void *arg) { \
		int start = (long long)arg; \
		int end = (start + wordNum <= MEM_SIZE) ? (start + wordNum) : MEM_SIZE; \
		int i = start; \
		while(i < end) { \
			int word = 0; \
			int j = 0; \
			while(j < 5) { \
				int ch = fgetc(file); \
				if(ch == EOF) \
					goto _deviceInExit; \
				if(ch >= ' ' && ch <= 'Z') { \
					word <<= 6; \
					word |= (ch - ' '); \
					++j; \
				} \
			} \
			memory[i++] = word; \
		} \
		_deviceInExit: \
		pthread_mutex_unlock(&mutex); \
		return NULL; \
	} \
	static void _device(int addr, void*(*entry)(void*)) { \
		pthread_mutex_lock(&mutex); \
		pthread_t id; \
		void *arg = (void*)((long long)addr); \
		if(pthread_create(&id, NULL, entry, arg)) { \
			printf("fail to create the thread about %s\n", #deviceName); \
			exit(1); \
		} \
	} \
	void deviceName##Out(int addr) { \
		_device(addr, _deviceOut); \
	} \
	void deviceName##In(int addr) { \
		_device(addr, _deviceIn); \
	} \
	void deviceName##Ioc() { \
		pthread_mutex_lock(&mutex); \
		pthread_mutex_unlock(&mutex); \
	}
